#include <iostream>
#include <memory>
#include <vector>
#include <optional>
#include <functional>
#include <vulkan/vulkan.hpp>

#include "SwapChain.hpp"

namespace vlk
{
    using CreateSurfaceFunction = std::function<vk::SurfaceKHR(vk::Instance)>;
    class Context final
    {
    private:
        static std::unique_ptr<Context> instance_;
        Context(const char *const *extensions, CreateSurfaceFunction callBack);
        void queryVulkanQueueFamilyIndicates();
        void getQueue();

    public:
        vk::Instance vkInstance;
        vk::PhysicalDevice physicalDevie;
        vk::Device logicDevice;
        vk::Queue graphicQueue;
        vk::Queue presentQueue;
        vk::SurfaceKHR surface;
        std::unique_ptr<SwapChain> swapChain_;

        struct QueueFamilyIndicates final
        {
            std::optional<uint32_t> graphicQueue;
            std::optional<uint32_t> presentQueue;
        };
        QueueFamilyIndicates queueFamilyIndicates;

        static void Init(const char *const *extensions, CreateSurfaceFunction callBack);
        static void Quit();
        static Context &GetInstance();
        ~Context();
        /* Create vulkan instance */
        void createVkInstance(const char *const *extensions);
        /* Choose a physical device */
        void pickPhysicalDevice();
        /* Create logical device */
        void createLogicalDevice();
        /* Create surface */
        void createSurface(CreateSurfaceFunction callBack);
        /* Init swap chain */
        void initSwapChain(int w, int h);
    };
}